1   /*
2    * Copyright (c) 2008, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.
8    *
9    * This code is distributed in the hope that it will be useful, but WITHOUT
10   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12   * version 2 for more details (a copy is included in the LICENSE file that
13   * accompanied this code).
14   *
15   * You should have received a copy of the GNU General Public License version
16   * 2 along with this work; if not, write to the Free Software Foundation,
17   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18   *
19   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20   * or visit www.oracle.com if you need additional information or have any
21   * questions.
22   */
23  
24  /**
25   * @test
26   * @bug 4823811
27   * @summary Confirm that text which includes numbers with a trailing minus sign is parsed correctly.
28   * @run main/othervm -Duser.timezone=GMT+09:00 Bug4823811
29   */
30  
31  import java.text.*;
32  import java.util.*;
33  
34  public class Bug4823811 {
35  
36      private static Locale localeEG = new Locale("ar", "EG");
37      private static Locale localeUS = Locale.US;
38  
39      private static String JuneInArabic = "\u064a\u0648\u0646\u064a\u0648";
40      private static String JulyInArabic = "\u064a\u0648\u0644\u064a\u0648";
41      private static String JuneInEnglish = "June";
42      private static String JulyInEnglish = "July";
43  
44      private static String BORDER =
45          "============================================================";
46  
47      /*
48       * I don't use static import here intentionally so that this test program
49       * can be run on JDK 1.4.2.
50       */
51      private static int ERA = Calendar.ERA;
52      private static int BC = GregorianCalendar.BC;
53  //    private static int JAN = Calendar.JANUARY;
54  //    private static int FEB = Calendar.FEBRUARY;
55  //    private static int MAR = Calendar.MARCH;
56      private static int APR = Calendar.APRIL;
57      private static int MAY = Calendar.MAY;
58      private static int JUN = Calendar.JUNE;
59      private static int JUL = Calendar.JULY;
60  //    private static int AUG = Calendar.AUGUST;
61  //    private static int SEP = Calendar.SEPTEMBER;
62  //    private static int OCT = Calendar.OCTOBER;
63  //    private static int NOV = Calendar.NOVEMBER;
64  //    private static int DEC = Calendar.DECEMBER;
65  
66      private static String[] patterns = {
67          "yyyy MMMM d H m s",
68          "yyyy MM dd hh mm ss",
69  
70          /*
71           * Because 1-based HOUR_OF_DAY, 1-based HOUR, MONTH, and YEAR fields
72           * are parsed using different code from the code for other numeric
73           * fields, I prepared YEAR-preceding patterns and SECOND-preceding
74           * patterns.
75           */
76          "yyyy M d h m s",
77          " yyyy M d h m s",
78          "yyyy M d h m s ",
79  
80          "s m h d M yyyy",
81          " s m h d M yyyy",
82          "s m h d M yyyy ",
83      };
84  
85      private static char originalMinusSign1 = ':';
86      private static char originalMinusSign2 = '\uff0d';  // fullwidth minus
87      private static String[] delimiters = {"-", "/", ":", "/", "\uff0d", "/"};
88      private static String[][] specialDelimiters = {
89          // for Arabic formatter and modified English formatter
90          {"--", "-/", "::", ":/", "\uff0d\uff0d", "\uff0d/"},
91  
92          // for English formatter and modified Arabic formatter
93          {"--", "/-", "::", "/:", "\uff0d\uff0d", "/\uff0d"},
94      };
95  
96      /*
97       * Format:
98       *   +-------------------------------------------------------------------+
99       *   | Input               | Output                                      |
100      *   +---------------------+---------------------------------------------|
101      *   | datesEG & datesUS   | formattedDatesEG & formattedDatesUS         |
102      *   +-------------------------------------------------------------------+
103      *
104      * Parse:
105      *   +-------------------------------------------------------------------+
106      *   | Input               | Output                                      |
107      *   |---------------------+---------------------------------------------|
108      *   | datesToParse        | datesEG & datesUS                           |
109      *   +-------------------------------------------------------------------+
110      */
111     private static String[][] datesToParse = {
112         // "JUNE" and "JULY" are replaced with a localized month name later.
113         {"2008 JULY 20 3 12 83",
114          "2008  JULY 20 3 12 83",
115          "2008 JULY  20 3 12 83"},
116 
117         {"2008 07 20 03 12 83",
118          "2008  07 20 03 12 83",
119          "2008 07  20 03 12 83"},
120 
121         {"2008 7 20 3 12 83",
122          "2008  7 20  3 12 83",
123          "2008 7  20  3 12 83"},
124 
125         {" 2008 7 20 3 12 83",
126          "  2008 7 20 3 12 83",
127          " 2008  7 20 3 12 83",
128          "2008 7 20 3 12 83"},
129 
130         {"2008 7 20 3 12 83 ",
131          "2008 7 20 3 12 83  ",
132          "2008 7 20 3 12 83"},
133 
134         {"83 12 3 20 7 2008",
135          "83  12 3  20 7 2008",
136          "83 12  3  20 7 2008"},
137 
138         {" 83 12 3 20 7 2008",
139          "  83 12 3 20 7 2008",
140          " 83  12 3 20 7 2008",
141          "83 12 3 20 7 2008"},
142 
143         {"83 12 3 20 7 2008 ",
144          "83 12 3 20 7 2008  ",
145          "83 12 3 20 7 2008"},
146     };
147 
148     // For formatting
149     private static String[][] formattedDatesEG = {
150         {"2008 JULY 20 3 13 23",
151          "2009 JULY 20 3 13 23",
152          null},
153 
154         {"2008 07 20 03 13 23",
155          "2009 07 20 03 13 23",
156          "2007 05 20 03 13 23"},
157 
158         {"2008 7 20 3 13 23",
159          "2009 6 10 3 13 23",
160          "2007 4 10 3 13 23"},
161 
162         {" 2008 7 20 3 13 23",
163          null,
164          " 2009 7 20 3 13 23",
165          null},
166 
167         {"2008 7 20 3 13 23 ",
168          "2008 7 20 3 10 37 ",
169          null},
170 
171         {"23 13 3 20 7 2008",
172          "37 10 9 19 7 2008",
173          "23 49 8 19 7 2008"},
174 
175         {" 23 13 3 20 7 2008",
176          null,
177          " 37 10 3 20 7 2008",
178          null},
179 
180         {"23 13 3 20 7 2008 ",
181          "23 13 3 20 7 2009 ",
182          null},
183     };
184 
185     private static String[][] formattedDatesUS = {
186         {"2008 JULY 20 3 13 23",
187          null,
188          "2008 JUNE 10 3 13 23"},
189 
190         {"2008 07 20 03 13 23",
191          "2007 05 20 03 13 23",
192          "2008 06 10 03 13 23"},
193 
194         {"2008 7 20 3 13 23",
195          "2007 5 19 9 13 23",
196          "2008 6 9 9 13 23"},
197 
198         {" 2008 7 20 3 13 23",
199          " 2009 7 20 3 13 23",
200          " 2007 5 20 3 13 23",
201          null},
202 
203         {"2008 7 20 3 13 23 ",
204          "2008 7 20 3 13 23 ",
205          null},
206 
207         {"23 13 3 20 7 2008",
208          "23 49 2 10 6 2008",
209          "23 13 9 9 6 2008"},
210 
211         {" 23 13 3 20 7 2008",
212          " 37 10 3 20 7 2008",
213          " 23 49 2 20 7 2008",
214          null},
215 
216         {"23 13 3 20 7 2008 ",
217          "23 13 3 20 7 2008 ",
218          null},
219     };
220 
221     private static GregorianCalendar[][] datesEG = {
222         {new GregorianCalendar( 2008, JUL,  20,  3,  12,  83),
223          new GregorianCalendar(-2008, JUL,  20,  3,  12,  83),
224          null},
225 
226         {new GregorianCalendar( 2008, JUL,  20,  3,  12,  83),
227          new GregorianCalendar(-2008, JUL,  20,  3,  12,  83),
228          new GregorianCalendar( 2007, MAY,  20,  3,  12,  83)},
229 
230         {new GregorianCalendar( 2008, JUL,  20,  3,  12,  83),
231          new GregorianCalendar(-2008, JUL, -20,  3,  12,  83),
232          new GregorianCalendar( 2007, APR,  10,  3,  12,  83)},
233 
234         {new GregorianCalendar( 2008, JUL,  20,  3,  12,  83),
235          null,
236          new GregorianCalendar(-2008, JUL,  20,  3,  12,  83),
237          null},
238 
239         {new GregorianCalendar( 2008, JUL,  20,  3,  12,  83),
240          new GregorianCalendar( 2008, JUL,  20,  3,  12, -83),
241          null},
242 
243         {new GregorianCalendar( 2008, JUL,  20,  3,  12,  83),
244          new GregorianCalendar( 2008, JUL,  20, -3,  12, -83),
245          new GregorianCalendar( 2008, JUL,  20, -3, -12,  83)},
246 
247         {new GregorianCalendar( 2008, JUL,  20,  3,  12,  83),
248          null,
249          new GregorianCalendar( 2008, JUL,  20,  3,  12, -83),
250          null},
251 
252         {new GregorianCalendar( 2008, JUL,  20,  3,  12,  83),
253          new GregorianCalendar(-2008, JUL,  20,  3,  12,  83),
254          null},
255     };
256 
257     private static GregorianCalendar[][] datesUS = {
258         {new GregorianCalendar( 2008, JUL,  20,  3,  12,  83),
259          null,
260          new GregorianCalendar( 2008, JUN,  10,  3,  12,  83)},
261 
262         {new GregorianCalendar( 2008, JUL,  20,  3,  12,  83),
263          new GregorianCalendar( 2007, MAY,  20,  3,  12,  83),
264          new GregorianCalendar( 2008, JUN,  10,  3,  12,  83)},
265 
266         {new GregorianCalendar( 2008, JUL,  20,  3,  12,  83),
267          new GregorianCalendar( 2007, MAY,  20, -3,  12,  83),
268          new GregorianCalendar( 2008, JUL, -20, -3,  12,  83)},
269 
270         {new GregorianCalendar( 2008, JUL,  20,  3,  12,  83),
271          new GregorianCalendar(-2008, JUL,  20,  3,  12,  83),
272          new GregorianCalendar( 2007, MAY,  20,  3,  12,  83),
273          null},
274 
275         {new GregorianCalendar( 2008, JUL,  20,  3,  12,  83),
276          new GregorianCalendar( 2008, JUL,  20,  3,  12,  83),
277          null},
278 
279         {new GregorianCalendar( 2008, JUL,  20,  3,  12,  83),
280          new GregorianCalendar( 2008, JUL, -20,  3, -12,  83),
281          new GregorianCalendar( 2008, JUL, -20, -3,  12,  83)},
282 
283         {new GregorianCalendar( 2008, JUL,  20,  3,  12,  83),
284          new GregorianCalendar( 2008, JUL,  20,  3,  12, -83),
285          new GregorianCalendar( 2008, JUL,  20,  3, -12,  83),
286          null},
287 
288         {new GregorianCalendar( 2008, JUL,  20,  3,  12,  83),
289          new GregorianCalendar( 2008, JUL,  20,  3,  12,  83),
290          null},
291     };
292 
293     /* flags */
294     private static boolean err = false;
295     private static boolean verbose = false;
296 
297 
298     public static void main(String[] args) {
299         if (args.length == 1 && args[0].equals("-v")) {
300             verbose = true;
301         }
302 
303         Locale defaultLocale = Locale.getDefault();
304         TimeZone defaultTimeZone = TimeZone.getDefault();
305 
306         TimeZone.setDefault(TimeZone.getTimeZone("Asia/Tokyo"));
307 
308         try {
309             /*
310              * Test SimpleDateFormat.parse() and format() for original
311              * SimpleDateFormat instances
312              */
313             testDateFormat1();
314 
315             /*
316              * Test SimpleDateFormat.parse() and format() for modified
317              * SimpleDateFormat instances using an original minus sign,
318              * pattern, and diffenrent month names in DecimalFormat
319              */
320             testDateFormat2();
321 
322             /*
323              * Test SimpleDateFormat.parse() and format() for modified
324              * SimpleDateFormat instances using a fullwidth minus sign
325              */
326             testDateFormat3();
327 
328             /*
329              * Just to confirm that regressions aren't introduced in
330              * DecimalFormat. This cannot happen, though. Because I didn't
331              * change DecimalFormat at all.
332              */
333             testNumberFormat();
334         }
335         catch (Exception e) {
336             err = true;
337             System.err.println("Unexpected exception: " + e);
338         }
339         finally {
340             Locale.setDefault(defaultLocale);
341             TimeZone.setDefault(defaultTimeZone);
342 
343             if (err) {
344                 System.err.println(BORDER + " Test failed.");
345                 throw new RuntimeException("Date/Number formatting/parsing error.");
346             } else {
347                 System.out.println(BORDER + " Test passed.");
348             }
349         }
350     }
351 
352 
353     //
354     // DateFormat test
355     //
356     private static void testDateFormat1() {
357         for (int i = 0; i < patterns.length; i++) {
358             System.out.println(BORDER);
359             for (int j = 0; j <= 1; j++) {
360                 // Generate a pattern
361                 String pattern = patterns[i].replaceAll(" ", delimiters[j]);
362                 System.out.println("Pattern=\"" + pattern + "\"");
363 
364                 System.out.println("*** DateFormat.format test in ar_EG");
365                 testDateFormatFormattingInRTL(pattern, i, j, null, localeEG, false);
366 
367                 System.out.println("*** DateFormat.parse test in ar_EG");
368                 testDateFormatParsingInRTL(pattern, i, j, null, localeEG, false);
369 
370                 System.out.println("*** DateFormat.format test in en_US");
371                 testDateFormatFormattingInLTR(pattern, i, j, null, localeUS, true);
372 
373                 System.out.println("*** DateFormat.parse test in en_US");
374                 testDateFormatParsingInLTR(pattern, i, j, null, localeUS, true);
375             }
376         }
377     }
378 
379     private static void testDateFormat2() {
380         /*
381          * modified ar_EG Date&Time formatter :
382          *   minus sign:  ':'
383          *   pattern:     "#,##0.###"
384          *   month names: In Arabic
385          *
386          * modified en_US Date&Time formatter :
387          *   minus sign:  ':'
388          *   pattern:     "#,##0.###;#,##0.###-"
389          *   month names: In English
390          */
391         DecimalFormat dfEG = (DecimalFormat)NumberFormat.getInstance(localeEG);
392         DecimalFormat dfUS = (DecimalFormat)NumberFormat.getInstance(localeUS);
393 
394         DecimalFormatSymbols dfsEG = dfEG.getDecimalFormatSymbols();
395         DecimalFormatSymbols dfsUS = dfUS.getDecimalFormatSymbols();
396         dfsEG.setMinusSign(originalMinusSign1);
397         dfsUS.setMinusSign(originalMinusSign1);
398         dfEG.setDecimalFormatSymbols(dfsUS);
399         dfUS.setDecimalFormatSymbols(dfsEG);
400 
401         String patternEG = dfEG.toPattern();
402         String patternUS = dfUS.toPattern();
403 
404         dfEG.applyPattern(patternUS);
405         dfUS.applyPattern(patternEG);
406 
407         for (int i = 0; i < patterns.length; i++) {
408             System.out.println(BORDER);
409             for (int j = 2; j <= 3; j++) {
410                 // Generate a pattern
411                 String pattern = patterns[i].replaceAll(" ", delimiters[j]);
412                 System.out.println("Pattern=\"" + pattern + "\"");
413 
414                 System.out.println("*** DateFormat.format test in modified en_US");
415                 testDateFormatFormattingInRTL(pattern, i, j, dfUS, localeUS, true);
416 
417                 System.out.println("*** DateFormat.parse test in modified en_US");
418                 testDateFormatParsingInRTL(pattern, i, j, dfUS, localeUS, true);
419 
420                 System.out.println("*** DateFormat.format test in modified ar_EG");
421                 testDateFormatFormattingInLTR(pattern, i, j, dfEG, localeEG, false);
422 
423                 System.out.println("*** DateFormat.parse test in modified ar_EG");
424                 testDateFormatParsingInLTR(pattern, i, j, dfEG, localeEG, false);
425             }
426         }
427     }
428 
429     private static void testDateFormat3() {
430         /*
431          * modified ar_EG Date&Time formatter :
432          *   minus sign:  '\uff0d'  // fullwidth minus
433          *   pattern:     "#,##0.###;#,##0.###-"
434          *   month names: In Arabic
435          *
436          * modified en_US Date&Time formatter :
437          *   minus sign:  '\uff0d'  // fullwidth minus
438          *   pattern:     "#,##0.###"
439          *   month names: In English
440          */
441         DecimalFormat dfEG = (DecimalFormat)NumberFormat.getInstance(localeEG);
442         DecimalFormat dfUS = (DecimalFormat)NumberFormat.getInstance(localeUS);
443 
444         DecimalFormatSymbols dfsEG = dfEG.getDecimalFormatSymbols();
445         DecimalFormatSymbols dfsUS = dfUS.getDecimalFormatSymbols();
446         dfsEG.setMinusSign(originalMinusSign2);
447         dfsUS.setMinusSign(originalMinusSign2);
448         dfEG.setDecimalFormatSymbols(dfsEG);
449         dfUS.setDecimalFormatSymbols(dfsUS);
450 
451         for (int i = 0; i < patterns.length; i++) {
452             System.out.println(BORDER);
453             for (int j = 4; j <= 5; j++) {
454                 // Generate a pattern
455                 String pattern = patterns[i].replaceAll(" ", delimiters[j]);
456                 System.out.println("Pattern=\"" + pattern + "\"");
457 
458                 System.out.println("*** DateFormat.format test in modified ar_EG");
459                 testDateFormatFormattingInRTL(pattern, i, j, dfEG, localeEG, false);
460 
461                 System.out.println("*** DateFormat.parse test in modified ar_EG");
462                 testDateFormatParsingInRTL(pattern, i, j, dfEG, localeEG, false);
463 
464                 System.out.println("*** DateFormat.format test in modified en_US");
465                 testDateFormatFormattingInLTR(pattern, i, j, dfUS, localeUS, true);
466 
467                 System.out.println("*** DateFormat.parse test in modified en_US");
468                 testDateFormatParsingInLTR(pattern, i, j, dfUS, localeUS, true);
469             }
470         }
471     }
472 
473     private static void testDateFormatFormattingInRTL(String pattern,
474                                                       int basePattern,
475                                                       int delimiter,
476                                                       NumberFormat nf,
477                                                       Locale locale,
478                                                       boolean useEnglishMonthName) {
479         Locale.setDefault(locale);
480 
481         SimpleDateFormat sdf = new SimpleDateFormat(pattern);
482         if (nf != null) {
483             sdf.setNumberFormat(nf);
484         }
485         for (int i = 0; i < datesToParse[basePattern].length; i++) {
486             if (datesEG[basePattern][i] == null) {
487                 continue;
488             }
489 
490             String expected = formattedDatesEG[basePattern][i]
491                               .replaceAll("JUNE", (useEnglishMonthName ?
492                                                    JuneInEnglish : JuneInArabic))
493                               .replaceAll("JULY", (useEnglishMonthName ?
494                                                    JulyInEnglish : JulyInArabic))
495                               .replaceAll(" ", delimiters[delimiter]);
496             testDateFormatFormatting(sdf, pattern, datesEG[basePattern][i],
497                 expected, locale.toString());
498         }
499     }
500 
501     private static void testDateFormatFormattingInLTR(String pattern,
502                                                       int basePattern,
503                                                       int delimiter,
504                                                       NumberFormat nf,
505                                                       Locale locale,
506                                                       boolean useEnglishMonthName) {
507         Locale.setDefault(locale);
508 
509         SimpleDateFormat sdf = new SimpleDateFormat(pattern);
510         if (nf != null) {
511             sdf.setNumberFormat(nf);
512         }
513         for (int i = 0; i < datesToParse[basePattern].length; i++) {
514             if (datesUS[basePattern][i] == null) {
515                 continue;
516             }
517 
518             String expected = formattedDatesUS[basePattern][i]
519                               .replaceAll("JUNE", (useEnglishMonthName ?
520                                                    JuneInEnglish : JuneInArabic))
521                               .replaceAll("JULY", (useEnglishMonthName ?
522                                                    JulyInEnglish : JulyInArabic))
523                               .replaceAll(" ", delimiters[delimiter]);
524             testDateFormatFormatting(sdf, pattern, datesUS[basePattern][i],
525                 expected, locale.toString());
526         }
527     }
528 
529     private static void testDateFormatFormatting(SimpleDateFormat sdf,
530                                                  String pattern,
531                                                  GregorianCalendar givenGC,
532                                                  String expected,
533                                                  String locale) {
534         Date given = givenGC.getTime();
535         String str = sdf.format(given);
536         if (expected.equals(str)) {
537             if (verbose) {
538                 System.out.print("  Passed: SimpleDateFormat(");
539                 System.out.print(locale + ", \"" + pattern + "\").format(");
540                 System.out.println(given + ")");
541 
542                 System.out.print("      ---> \"" + str + "\" ");
543                 System.out.println((givenGC.get(ERA) == BC) ? "(BC)" : "(AD)");
544             }
545         } else {
546             err = true;
547 
548             System.err.print("  Failed: Unexpected SimpleDateFormat(");
549             System.out.print(locale + ", \"" + pattern + "\").format(");
550             System.out.println(given + ") result.");
551 
552             System.out.println("      Expected: \"" + expected + "\"");
553 
554             System.out.print("      Got:      \"" + str + "\" ");
555             System.out.println((givenGC.get(ERA) == BC) ? "(BC)" : "(AD)");
556         }
557     }
558 
559     private static void testDateFormatParsingInRTL(String pattern,
560                                                    int basePattern,
561                                                    int delimiter,
562                                                    NumberFormat nf,
563                                                    Locale locale,
564                                                    boolean useEnglishMonthName) {
565         Locale.setDefault(locale);
566 
567         SimpleDateFormat sdf = new SimpleDateFormat(pattern);
568         if (nf != null) {
569             sdf.setNumberFormat(nf);
570         }
571         for (int i = 0; i < datesToParse[basePattern].length; i++) {
572             String given = datesToParse[basePattern][i]
573                            .replaceAll("  ", specialDelimiters[0][delimiter])
574                            .replaceAll(" ", delimiters[delimiter]);
575 
576             testDateFormatParsing(sdf, pattern,
577                 given.replaceAll("JULY", (useEnglishMonthName ?
578                                           JulyInEnglish :  JulyInArabic)),
579                 datesEG[basePattern][i], locale.toString());
580         }
581     }
582 
583     private static void testDateFormatParsingInLTR(String pattern,
584                                                    int basePattern,
585                                                    int delimiter,
586                                                    NumberFormat nf,
587                                                    Locale locale,
588                                                    boolean useEnglishMonthName) {
589         Locale.setDefault(locale);
590 
591         SimpleDateFormat sdf = new SimpleDateFormat(pattern);
592         if (nf != null) {
593             sdf.setNumberFormat(nf);
594         }
595         for (int i = 0; i < datesToParse[basePattern].length; i++) {
596             String given = datesToParse[basePattern][i]
597                            .replaceAll("  ", specialDelimiters[1][delimiter])
598                            .replaceAll(" ", delimiters[delimiter]);
599 
600             testDateFormatParsing(sdf, pattern,
601                 given.replaceAll("JULY", (useEnglishMonthName ?
602                                           JulyInEnglish :  JulyInArabic)),
603                 datesUS[basePattern][i], locale.toString());
604         }
605     }
606 
607     private static void testDateFormatParsing(SimpleDateFormat sdf,
608                                               String pattern,
609                                               String given,
610                                               GregorianCalendar expectedGC,
611                                               String locale) {
612         try {
613             Date d = sdf.parse(given);
614             if (expectedGC == null) {
615                 err = true;
616                 System.err.print("  Failed: SimpleDateFormat(" + locale);
617                 System.err.print(", \"" + pattern + "\").parse(\"" + given);
618                 System.err.println("\") should have thrown ParseException");
619             } else if (expectedGC.getTime().equals(d)) {
620                 if (verbose) {
621                     System.out.print("  Passed: SimpleDateFormat(" + locale);
622                     System.out.print(", \"" + pattern + "\").parse(\"" + given);
623                     System.out.println("\")");
624 
625                     System.out.print("      ---> " + d + " (" + d.getTime());
626                     System.out.println(")");
627                 }
628             } else {
629                 err = true;
630                 System.err.print("  Failed: SimpleDateFormat(" + locale);
631                 System.err.print(", \"" + pattern + "\").parse(\"" + given);
632                 System.err.println("\")");
633 
634                 System.err.print("      Expected: " + expectedGC.getTime());
635                 System.err.println(" (" + d.getTime() + ")");
636 
637                 System.err.print("      Got:      " + d + " (" + d.getTime());
638                 System.err.println(")");
639 
640                 System.err.print("      Pattern:  \"");
641                 System.err.print(((DecimalFormat)sdf.getNumberFormat()).toPattern());
642                 System.err.println("\"");
643             }
644         }
645         catch (ParseException pe) {
646             if (expectedGC == null) {
647                 if (verbose) {
648                     System.out.print("  Passed: SimpleDateFormat(" + locale);
649                     System.out.print(", \"" + pattern + "\").parse(\"" + given);
650                     System.out.println("\")");
651 
652                     System.out.println("      threw ParseException as expected");
653                 }
654             } else {
655                 err = true;
656                 System.err.println("  Failed: Unexpected exception with");
657 
658                 System.err.print("    SimpleDateFormat(" + locale);
659                 System.err.print(", \"" + pattern + "\").parse(\"");
660                 System.err.println(given + "\"):");
661 
662                 System.err.println("      " + pe);
663 
664                 System.err.print("      Pattern: \"");
665                 System.err.print(((DecimalFormat)sdf.getNumberFormat()).toPattern());
666                 System.err.println("\"");
667 
668                 System.err.print("      Month 0: ");
669                 System.err.println(sdf.getDateFormatSymbols().getMonths()[0]);
670             }
671         }
672     }
673 
674 
675     //
676     // NumberFormat test
677     //
678     private static void testNumberFormat() {
679         NumberFormat nfEG = NumberFormat.getInstance(localeEG);
680         NumberFormat nfUS = NumberFormat.getInstance(localeUS);
681 
682         System.out.println("*** DecimalFormat.format test in ar_EG");
683         testNumberFormatFormatting(nfEG, -123456789, "123,456,789-", "ar_EG");
684         testNumberFormatFormatting(nfEG, -456, "456-", "ar_EG");
685 
686         System.out.println("*** DecimalFormat.parse test in ar_EG");
687         testNumberFormatParsing(nfEG, "123-", new Long(-123), "ar_EG");
688         testNumberFormatParsing(nfEG, "123--", new Long(-123), "ar_EG");
689         testNumberFormatParsingCheckException(nfEG, "-123", 0, "ar_EG");
690 
691         System.out.println("*** DecimalFormat.format test in en_US");
692         testNumberFormatFormatting(nfUS, -123456789, "-123,456,789", "en_US");
693         testNumberFormatFormatting(nfUS, -456, "-456", "en_US");
694 
695         System.out.println("*** DecimalFormat.parse test in en_US");
696         testNumberFormatParsing(nfUS, "123-", new Long(123), "en_US");
697         testNumberFormatParsing(nfUS, "-123", new Long(-123), "en_US");
698         testNumberFormatParsingCheckException(nfUS, "--123", 0, "en_US");
699     }
700 
701     private static void testNumberFormatFormatting(NumberFormat nf,
702                                                    int given,
703                                                    String expected,
704                                                    String locale) {
705         String str = nf.format(given);
706         if (expected.equals(str)) {
707             if (verbose) {
708                 System.out.print("  Passed: NumberFormat(" + locale);
709                 System.out.println(").format(" + given + ")");
710 
711                 System.out.println("      ---> \"" + str + "\"");
712             }
713         } else {
714             err = true;
715             System.err.print("  Failed: Unexpected NumberFormat(" + locale);
716             System.err.println(").format(" + given + ") result.");
717 
718             System.err.println("      Expected: \"" + expected + "\"");
719 
720             System.err.println("      Got:      \"" + str + "\"");
721         }
722     }
723 
724     private static void testNumberFormatParsing(NumberFormat nf,
725                                                 String given,
726                                                 Number expected,
727                                                 String locale) {
728         try {
729             Number n = nf.parse(given);
730             if (n.equals(expected)) {
731                 if (verbose) {
732                     System.out.print("  Passed: NumberFormat(" + locale);
733                     System.out.println(").parse(\"" + given + "\")");
734 
735                     System.out.println("      ---> " + n);
736                 }
737             } else {
738                 err = true;
739                 System.err.print("  Failed: Unexpected NumberFormat(" + locale);
740                 System.err.println(").parse(\"" + given + "\") result.");
741 
742                 System.err.println("      Expected: " + expected);
743 
744                 System.err.println("      Got:      " + n);
745             }
746         }
747         catch (ParseException pe) {
748             err = true;
749             System.err.print("  Failed: Unexpected exception with NumberFormat(");
750             System.err.println(locale + ").parse(\"" + given + "\") :");
751 
752             System.err.println("    " + pe);
753         }
754     }
755 
756     private static void testNumberFormatParsingCheckException(NumberFormat nf,
757                                                               String given,
758                                                               int expected,
759                                                               String locale) {
760         try {
761             Number n = nf.parse(given);
762             err = true;
763 
764             System.err.print("  Failed: NumberFormat(" + locale);
765             System.err.println(").parse(\"" + given + "\")");
766 
767             System.err.println("      should have thrown ParseException");
768         }
769         catch (ParseException pe) {
770             int errorOffset = pe.getErrorOffset();
771             if (errorOffset == expected) {
772                 if (verbose) {
773                     System.out.print("  Passed: NumberFormat(" + locale);
774                     System.out.println(").parse(\"" + given + "\")");
775 
776                     System.out.print("      threw ParseException as expected, and its errorOffset was correct: ");
777                     System.out.println(errorOffset);
778                 }
779             } else {
780                 err = true;
781                 System.err.print("  Failed: NumberFormat(" + locale);
782                 System.err.println(").parse(\"" + given + "\")");
783 
784                 System.err.print("      threw ParseException as expected, but its errorOffset was incorrect: ");
785                 System.err.println(errorOffset);
786             }
787         }
788     }
789 
790 }